package cn.wolfcode.wolf2w.business.service.impl;

import cn.wolfcode.wolf2w.business.api.RemoteDestinationService;
import cn.wolfcode.wolf2w.business.api.RemoteNoteService;
import cn.wolfcode.wolf2w.business.api.RemoteStrategyService;
import cn.wolfcode.wolf2w.business.api.domain.*;
import cn.wolfcode.wolf2w.business.query.SearchQuery;
import cn.wolfcode.wolf2w.business.repository.DestinationEsRepository;
import cn.wolfcode.wolf2w.business.repository.NoteEsRepository;
import cn.wolfcode.wolf2w.business.repository.StrategyEsRepository;
import cn.wolfcode.wolf2w.business.repository.UserEsRepository;
import cn.wolfcode.wolf2w.business.service.ISearchService;

import cn.wolfcode.wolf2w.common.core.constant.SecurityConstants;
import cn.wolfcode.wolf2w.member.api.RemoteUserInfoService;
import cn.wolfcode.wolf2w.member.api.domain.UserInfo;
import org.apache.commons.beanutils.BeanUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements ISearchService {
    /*操作es的两种方式*/
    @Autowired
    private StrategyEsRepository strategyEsRepository;
    @Autowired
    private UserEsRepository userEsRepository;
    @Autowired
    private NoteEsRepository noteEsRepository;
    @Autowired
    private DestinationEsRepository destinationEsRepository;
    //用于执行高亮等复杂查询
    @Autowired
    private ElasticsearchRestTemplate template;
    @Autowired
    private RemoteStrategyService remoteStrategyService;
    @Resource
    private RemoteUserInfoService remoteUserEsService;
    @Autowired
    private RemoteNoteService remoteNoteService;
    @Autowired
    private RemoteDestinationService remoteDestinationService;

    @Override
    public void saveStrategy(StrategyES strategyES) {
       strategyEsRepository.save(strategyES);

    }

    @Override
    public <T> Page<T> searchWithHighLight(Class<T> clazz, Class<?> esClazz, SearchQuery qo, String... fields) throws InvocationTargetException, IllegalAccessException {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //设置查询条件
        builder.withQuery(
                //使用多列匹配，参数一：查询的内容，参数二：查询哪些列
                QueryBuilders.multiMatchQuery(qo.getKeyword(), fields)
        );
        //设置分页条件
        /*非关系型数据库的页数都是从0开始*/
        Pageable pageable = PageRequest.of(qo.getCurrentPage()-1, qo.getPageSize());
        builder.withPageable(pageable);
        //高亮查询--设置高亮条件
        /*fs集合中存储高亮字段配置的列表*/
        List<HighlightBuilder.Field> fs=new ArrayList<>();
        for (String name : fields) {//比如"nickname", "info", "city"
            //给高亮字段创建一个高亮字段对象
            HighlightBuilder.Field field = new HighlightBuilder.Field(name);
            field.preTags("<span style='color:red'>").postTags("</span>");//匹配到关键词的部分会被包裹在高亮标签中
            fs.add(field);
        }
        builder.withHighlightFields(fs);
        List<T> list=new ArrayList<>();
        //查询
        /*es的所有查询默认都是分页的
        * 查询库--数据量一般都很大
        * 非关系型数据库--①迭代器--一点点迭代获取
        *               ②分页查--获取所有数据然后分页默认100条--es
        *
        * */
        // 执行Elasticsearch查询，返回SearchHits对象，包含查询结果和元数据
        SearchHits<?> hits = template.search(builder.build(), esClazz);
        // 获取查询结果的总记录数
        long totalCount = hits.getTotalHits();

        for (SearchHit<?> searchHit : hits.getSearchHits()) {
            Long id = Long.valueOf(searchHit.getId());
            T t=null;//用于存储Mysql中的对象
            if (clazz.equals(Strategy.class)){
                t= (T) remoteStrategyService.getOne( id, SecurityConstants.INNER).getData();



            }
            if (clazz.equals(UserInfo.class)){
                t= (T) remoteUserEsService.getOne( id, SecurityConstants.INNER).getData();


            }
            if (clazz.equals(Note.class)){
                t= (T) remoteNoteService.getOne( id, SecurityConstants.INNER).getData();


            }
            if (clazz.equals(Destination.class)){
                t= (T) remoteDestinationService.getOne( id, SecurityConstants.INNER).getData();
            }
            list.add( t);
            /*将 ES 高亮结果替换到实体中*/
            //不确定取回来具体哪些字段被高亮
            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
            /*遍历出来的key--高亮的字段*/
            for (String key : highlightFields.keySet()) {
                /*	高亮片段列表（List<String> 类型），存储 key 字段中所有匹配关键词的高亮文本（可能有多个片段）。*/
                List<String> arr = highlightFields.get(key);
                StringBuffer sb = new StringBuffer();
                for (String s : arr) {
                    //看起来美观一些--为何不是换行，如果是换行，用户那边不美观
                    sb.append(s).append(" ");
                }
                String value=sb.toString();
                //注意选择apache的BeanUtils，设置t对象的key属性为value值
                //他和链式调用冲突
                BeanUtils.setProperty(t, key, value);
            }
        }
        /*构建分页对象，
        * 参数1：当前页数据
        * 参数2：分页条件
        * 参数3：总记录数
        * 一个分页对象中的数据
        * 当前页、每页条数、总条数、总页数、当前页数据、上一页、下一页
        *
        * pageable：当前页
        * list：当前页数据
        * totalPages：总页数
        * 总页数=总记录数%每页条数==0?总记录数/每页条数:总记录数/每页条数+1
        * 通过这三都能计算出来其他的分页条件数据
        * */
        return new PageImpl<T>(list, pageable, totalCount);
    }

    @Override
    public void saveUserES(UserES userES) {
        userEsRepository.save(userES);
    }

    @Override
    public void saveNoteEs(NoteES noteES) {
        noteEsRepository.save(noteES);
    }

    @Override
    public void saveDestinationEs(DestinationES destinationES) {
        destinationEsRepository.save(destinationES);
    }


}
